package xlog

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
)

// 表示没有对结构化的字段设置指定输出格式，仅使用空格分隔
var FmtEmptySeparate = ""

type Level uint8

const (
	DebugLevel Level = iota
	InfoLevel
	WarnLevel
	ErrorLevel
	PanicLevel
	FatalLevel
)

var LevelNameMapping = map[Level]string{
	DebugLevel: "DEBUG",
	InfoLevel:  "INFO",
	WarnLevel:  "WARN",
	ErrorLevel: "ERROR",
	PanicLevel: "PANIC",
	FatalLevel: "FATAL",
}

var errUnmarshalNilLevel = errors.New("can't unmarshal a nil *Level")

func (l *Level) unmarshalText(text []byte) bool {
	switch string(text) {
	case "DEBUG":
		*l = DebugLevel
	case "INFO":
		*l = InfoLevel
	case "WARN":
		*l = WarnLevel
	case "ERROR":
		*l = ErrorLevel
	case "PANIC":
		*l = PanicLevel
	case "FATAL":
		*l = FatalLevel
	default:
		return false
	}
	return true
}

func (l *Level) UnmarshalText(text []byte) error {
	if l == nil {
		return errUnmarshalNilLevel
	}

	if !l.unmarshalText(text) || !l.unmarshalText(bytes.ToLower(text)) {
		return fmt.Errorf("unrecognized level: %q", text)
	}

	return nil
}

func (l *Level) String() string {
	switch *l {
	case DebugLevel:
		return "DEBUG"
	case InfoLevel:
		return "INFO"
	case WarnLevel:
		return "WARN"
	case ErrorLevel:
		return "ERROR"
	case PanicLevel:
		return "PANIC"
	case FatalLevel:
		return "FATAL"
	default:
		return "UNKNOWN"
	}
}

type options struct {
	level    Level // 日志开关级别
	stdLevel Level // 使用全局 std 日志对象时的写日志级别

	formatter     Formatter // 日志文本格式
	disableCaller bool
	output        io.Writer // 日志输出方式
}

type Option func(o *options)

func initOptions(opts ...Option) (o *options) {
	o = &options{}
	for _, opt := range opts {
		opt(o)
	}

	if o.output == nil {
		o.output = os.Stderr
	}

	if o.formatter == nil {
		o.formatter = &TextFormatter{}
	}

	return
}

func WithOutput(output io.Writer) Option {
	return func(o *options) {
		o.output = output
	}
}

func WithLevel(l Level) Option {
	return func(o *options) {
		o.level = l
	}
}

func WithStdLevel(l Level) Option {
	return func(o *options) {
		o.stdLevel = l
	}
}

func WithFormatter(formatter Formatter) Option {
	return func(o *options) {
		o.formatter = formatter
	}
}

func WithDisableCaller(caller bool) Option {
	return func(o *options) {
		o.disableCaller = caller
	}
}
